package com.doraemon.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.doraemon.constant.MessageConstant;
import com.doraemon.constant.StatusConstant;
import com.doraemon.dto.DishDTO;
import com.doraemon.dto.DishPageQueryDTO;
import com.doraemon.entity.*;
import com.doraemon.exception.DeletionNotAllowedException;
import com.doraemon.mapper.DishFlavorMapper;
import com.doraemon.mapper.DishMapper;
import com.doraemon.mapper.SetmealDishMapper;
import com.doraemon.mapper.SetmealMapper;
import com.doraemon.result.PageResult;
import com.doraemon.service.DishService;
import com.doraemon.vo.DishVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * 菜品管理Service实现
 **/
@Service
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private DishFlavorMapper dishFlavorMapper;
    @Autowired
    private SetmealDishMapper setmealDishMapper;
    @Autowired
    private SetmealMapper setmealMapper;

    /***
     * 新增菜品和对应口味
     * -由Spring事务管理-
     * ---
     * @param dishDTO 菜品DTO类（前端参数）
     * @return: void
     */
    @Transactional
    @Override
    public void addDishWithFlavour(DishDTO dishDTO) {

        // 1.创建菜品实体
        Dish dish = new Dish();

        // 2.将菜品DTO拷贝到菜品实体
        BeanUtils.copyProperties(dishDTO, dish);

        /***
         * 由AOP处理
         */
        // 设置创建时间、修改时间、创建人、修改人

        // 3.调用Mapper层，向菜品表插入数据
        dishMapper.insert(dish);

        // 4.获取菜品插入之后的id（主键回显）
        Long dishId = dish.getId();

        // 5.获取菜品对应的口味集合
        List<DishFlavor> dishFlavors = dishDTO.getFlavors();

        // 6.口味集合进行非空非null判断
        if (dishFlavors == null || dishFlavors.size() == 0) {
            return; // 如果为空或非null则结束方法，不用向菜品口味表插入数据
        }

        // 7.对每个口味插入对应的菜品id
        for (DishFlavor flavor : dishFlavors) {
            flavor.setDishId(dishId);
        }

        // 8.调用Mapper层，向菜品口味表插入数据
        dishFlavorMapper.insertBatch(dishFlavors);
    }

    /***
     * 分页查询菜品
     * ---
     * @param dishPageQueryDTO 菜品分页DTO类
     * @return: com.doraemon.result.PageResult 分页结果集对象
     */
    @Override
    public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {

        // 1.使用PageHelper插件
        // 传递页码和每页展示的记录数，自动对SQL语句进行改写
        PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());

        // 2.执行菜品信息的查询获取Page对象
        Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);

        // 3.调用Page类型的方法来获取总记录数和结果集，并封装成PageResult返回
        return new PageResult(page.getTotal(), page.getResult());
    }

    /***
     * 批量删除菜品
     * -由Spring事务管理-
     * ---
     * @param ids 菜品id集合
     * @return: void
     */
    @Transactional
    @Override
    public void deleteBatch(List<Long> ids) {

        // 1.判断菜品是否能被删除（是否存在起售菜品）
        for (Long id : ids) {
            Dish dish = dishMapper.getById(id); // 调用Mapper层，根据菜品id查询菜品实体
            if (dish.getStatus() == StatusConstant.ENABLE) { // 如果菜品为起售状态，抛出自定义异常和自定义异常信息
                throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
            }
        }

        // 2.判断菜品是否能被删除（是否关联套餐）
        // 调用Mapper层，根据 菜品id集合 获取对应的套餐id集合
        List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishIds(ids);

        if (setmealIds != null && setmealIds.size() > 0) { // 如果菜品有关联的套餐，抛出自定义异常和自定义异常信息
            throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
        }

        // 3.调用Mapper层，删除菜品及对应的口味
        for (Long id : ids) {
            dishMapper.deleteById(id); // 根据菜品id删除菜品
            dishFlavorMapper.deleteByDishId(id); // 根据菜品id删除口味
        }
    }

    /***
     * 根据id查询菜品和对应的口味
     * ---
     * @param id 菜品id
     * @return: com.doraemon.vo.DishVO 菜品VO类
     */
    @Override
    public DishVO getByIdWithFlavor(Long id) {

        // 1.调用菜品Mapper层，根据菜品id获取菜品实体
        Dish dish = dishMapper.getById(id);

        // 2.调用菜品口味Mapper层，根据菜品id获取菜品口味实体集合
        List<DishFlavor> dishFlavors = dishFlavorMapper.getListByDishId(id);

        // 3.将查询到的数据封装到DishVO类
        DishVO dishVO = new DishVO();
        BeanUtils.copyProperties(dish, dishVO); // 调用工具类BeanUtils，将菜品实体拷贝到菜品VO类
        dishVO.setFlavors(dishFlavors); // 将菜品口味数据赋值VO类

        return dishVO;
    }

    /***
     * 修改菜品及对应的口味
     * -由Spring事务-
     * ---
     * @param dishDTO 菜品DTO类
     * @return: void
     */
    @Transactional
    @Override
    public void updateWithFlavor(DishDTO dishDTO) {

        // 1.创建菜品实体
        Dish dish = new Dish();

        // 2.调用工具类BeanUtils，将菜品DTO类拷贝到菜品实体
        BeanUtils.copyProperties(dishDTO, dish);

        // 3.调用菜品Mapper层，修改菜品
        dishMapper.update(dish);

        // 4.删除原有口味数据
        dishFlavorMapper.deleteByDishId(dish.getId());

        // 5.获取菜品对应的口味集合
        List<DishFlavor> dishFlavors = dishDTO.getFlavors();

        // 6.口味集合进行非空非null判断
        if (dishFlavors == null || dishFlavors.size() == 0) {
            return; // 如果为空或非null则结束方法，不用向菜品口味表插入数据
        }

        // 7.重新插入口味数据
        for (DishFlavor flavor : dishFlavors) { // 对每个口味插入对应的菜品id
            flavor.setDishId(dish.getId());
        }

        // 8.调用Mapper层，向菜品口味表插入数据
        dishFlavorMapper.insertBatch(dishFlavors);
    }

    /***
     * 修改菜品售卖状态
     * -由Spring事务管理-
     * ---
     * @param status 菜品售卖状态 0 停售 1 起售
     * @param id 菜品id
     * @return: com.doraemon.result.Result<java.lang.String>
     */
    @Transactional
    @Override
    public void updateStatus(Integer status, Long id) {

        // 1.根据status，id属性创建菜品实体
        Dish dish = new Dish();
        dish.setStatus(status);
        dish.setId(id);

        // 2.调用菜品Mapper层，更新菜品
        dishMapper.update(dish);

        // 3.判断停售状态菜品是否关联套餐
        if (dish.getStatus() == StatusConstant.DISABLE) {
            ArrayList dishIds = new ArrayList<>();
            dishIds.add(id);

            // 4.调用套餐菜品Mapper层，获取对应的套餐id集合
            List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishIds(dishIds);

            // 5.如果套餐内含有停售商品，其套餐也一并停售
            if (setmealIds != null && setmealIds.size() > 0) {

                for (Long setmealId : setmealIds) {
                    // 6.根据status，id属性创建套餐实体
                    Setmeal setmeal = new Setmeal();
                    setmeal.setId(setmealId);
                    setmeal.setStatus(StatusConstant.DISABLE);

                    // 7.调用套餐Mapper层，更新套餐
                    setmealMapper.update(setmeal);
                }
            }
        }
    }

    /***
     * 根据分类id查询菜品
     * ---
     * @param categoryId 分类id
     * @return: com.doraemon.result.Result<java.util.List < com.doraemon.entity.Dish>> 菜品实体集合
     */
    @Override
    public List<Dish> getListByCategoryId(Long categoryId) {

        // 调用Mapper层，根据分类id查询菜品实体集合
        List<Dish> dishes = dishMapper.getListByCategoryId(categoryId);

        return dishes;
    }

    /***
     * 通过菜品实体查询起售状态的菜品VO类集合
     * ---
     * @param dish 菜品实体
     * @return: java.util.List<com.doraemon.vo.DishVO>
     */
    @Override
    public List<DishVO> getVOListByDish(Dish dish) {

        // 1.调用菜品Mapper层，获取起售状态的菜品集合
        List<Dish> dishes = dishMapper.list(dish);

        // 2.创建菜品VO类集合
        List<DishVO> dishVOs = new ArrayList<>();

        // 3.循环将菜品实体属性赋给菜品VO类
        for (Dish d : dishes) {
            DishVO dishVO = new DishVO();
            BeanUtils.copyProperties(d, dishVO);

            // 4.调用菜品口味Mapper层，根据菜品id获取口味数据集合
            List<DishFlavor> dishFlavors = dishFlavorMapper.getListByDishId(d.getId());

            // 5.将菜品口味属性赋值给菜品VO类
            dishVO.setFlavors(dishFlavors);

            // 6.将菜品VO类添加到菜品VO类集合
            dishVOs.add(dishVO);
        }

        return dishVOs;
    }

}
